﻿using Zpb;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using gpb = global::Google.Protobuf;
using ConfClient;

namespace client
{
    public partial class SocketClient
    {
        Socket clientSocket;
        private int BufferLen = 409600;
        private byte[] ReadM = new byte[409600];
        private int recvoffest = 0;

        public bool SocketStart()
        {
            try
            {
                //实例化socket
                clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //连接服务器
                clientSocket.Connect("127.0.0.1", 8999);

                clientSocket.BeginReceive(ReadM, recvoffest, BufferLen - recvoffest, SocketFlags.None, new AsyncCallback(ReceiveCallBack), ReadM);

                return true;
            }
            catch (Exception ex)
            {
                App.Debug("服务器异常:" + ex.Message);
                return false;
            }
        }
        private void ReceiveCallBack(IAsyncResult ar)
        {
            int readCount = 0;

            try
            {
                //读取消息长度
                readCount = clientSocket.EndReceive(ar);//调用这个函数来结束本次接收并返回接收到的数据长度。
                recvoffest += readCount;
                if (recvoffest > BufferLen)
                {
                    App.Debug($"出现Socket异常");
                    return;
                }

                if (readCount <= 0)
                {
                    recvoffest = 0;
                    //disconnect();
                    return;
                }

                DeCodePacket(ref ReadM, ref recvoffest);
            }
            catch (Exception ex)//出现Socket异常就关闭连接
            {
                recvoffest = 0;
                App.Debug($"出现Socket异常 {ex.Message}");
                return;
            }
            clientSocket.BeginReceive(ReadM, recvoffest, BufferLen - recvoffest, SocketFlags.None, new AsyncCallback(ReceiveCallBack), ReadM);
        }
        private int DeCodePacket(ref byte[] buf, ref int recvOffest)
        {
            //Info($"DeCodePacket:  buf:{BitConverter.ToString(buf)}");
            int decodeOffest = 0;
            int datalen = 0;
            UInt16 pid = 0;
            UInt16 code = 0;
            while (recvOffest - decodeOffest >= 8)
            {
                datalen = BitConverter.ToInt32(buf, decodeOffest);
                pid = BitConverter.ToUInt16(buf, decodeOffest + 4);
                code = BitConverter.ToUInt16(buf, decodeOffest + 6);

                if (datalen > recvOffest - (decodeOffest + 8))
                {
                    //continue recv packet
                    break;
                }

                decodeOffest += 8;
                byte[] buffer = new byte[datalen];
                Buffer.BlockCopy(buf, decodeOffest, buffer, 0, datalen);
                decodeOffest += datalen;

                MsgReceive(datalen, pid, code, buffer);
            }

            if (decodeOffest > 0 && decodeOffest < recvOffest)
            {
                Buffer.BlockCopy(buf, decodeOffest, buf, 0, recvOffest - decodeOffest);
            }

            recvOffest = recvOffest - decodeOffest;

            return 1;
        }
        
        public ConcurrentQueue<MsgData> ListMsg = new ConcurrentQueue<MsgData>();
        private void MsgReceive(int datalen, UInt16 pid, UInt16 code, byte[] buffer)
        {
            //var str = Encoding.Default.GetString(buffer);
            //Info("接收到服务器数据: " + str);
            //Info("接收到服务器数据: " + BitConverter.ToString(buffer));

            //App.Info($"接收到服务器数据:  datalen:{datalen} pid:{pid} buffer:{BitConverter.ToString(buffer)}");
            //MsgHandle((EPid)pid, buffer);
            App.Debug($"接收数据:  datalen:{datalen} pid:{(EPid)pid}:{pid} code:{(EProtocolResult)code}:{code}");
            var msg = new MsgData();
            msg.pid = (EPid)pid;
            msg.code = (EProtocolResult)code;
            msg.buf = buffer;
            ListMsg.Enqueue(msg);
        }
        //向服务器发送协议ID
        public void Send(EPid pid)
        {
            try
            {
                NetPack pack = new();
                pack.Encode((UInt16)pid);

                SendNetPack(pack);
            }
            catch
            {
            }

        }
        //向服务器发送数据
        public void Send<T>(EPid pid, T msg) where T : gpb.IMessage
        {
            try
            {
                MemoryStream msgStream = new MemoryStream();
                gpb::CodedOutputStream outputStream = new gpb::CodedOutputStream(msgStream);
                msg.WriteTo(outputStream);
                outputStream.Flush();

                NetPack pack = new();
                pack.Encode((UInt16)pid, msgStream.ToArray());

                SendNetPack(pack);
            }
            catch
            {
            }

        }
        //向服务器发送协议ID
        public void SendNetPack(NetPack pack)
        {
            try
            {
                //创建NetWorkStream
                NetworkStream ns;
                //加锁，避免在多线程下出问题
                lock (clientSocket)
                {
                    ns = new NetworkStream(clientSocket);
                }
                ns.BeginWrite(pack.Data, 0, pack.Data.Length, new AsyncCallback(SendCallBack), ns);
                App.Debug($"发送消息:  datalen:{pack.Data.Length} pid:{(EPid)pack.Pid}:{pack.Pid}");
            }
            catch
            {
                clientSocket.Close();
            }

        }
        public void SendCallBack(IAsyncResult ar)
        {
            NetworkStream ns = (NetworkStream)ar.AsyncState;
            try
            {
                ns.EndWrite(ar);
                ns.Flush();
                ns.Close();
            }
            catch
            {
                clientSocket.Close();
            }
        }
    }
    public class MsgData
    {
        public EPid pid;
        public EProtocolResult code;
        public byte[] buf;
    }
}
